"""Platform for Miele integration."""

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import Any, Final, cast

from aiohttp import ClientResponseError
from pymiele import MieleDevice, MieleTemperature

from homeassistant.components.climate import (
    ClimateEntity,
    ClimateEntityDescription,
    ClimateEntityFeature,
    HVACMode,
)
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType

from .const import DEVICE_TYPE_TAGS, DISABLED_TEMP_ENTITIES, DOMAIN, MieleAppliance
from .coordinator import MieleConfigEntry, MieleDataUpdateCoordinator
from .entity import MieleEntity

PARALLEL_UPDATES = 1

_LOGGER = logging.getLogger(__name__)


def _get_temperature_value(
    temperatures: list[MieleTemperature], index: int
) -> float | None:
    """Return the temperature value for the given index."""
    if len(temperatures) > index:
        return cast(int, temperatures[index].temperature) / 100.0
    return None


@dataclass(frozen=True, kw_only=True)
class MieleClimateDescription(ClimateEntityDescription):
    """Class describing Miele climate entities."""

    value_fn: Callable[[MieleDevice], StateType]
    target_fn: Callable[[MieleDevice], StateType]
    zone: int = 1


@dataclass
class MieleClimateDefinition:
    """Class for defining climate entities."""

    types: tuple[MieleAppliance, ...]
    description: MieleClimateDescription


CLIMATE_TYPES: Final[tuple[MieleClimateDefinition, ...]] = (
    MieleClimateDefinition(
        types=(
            MieleAppliance.FRIDGE,
            MieleAppliance.FREEZER,
            MieleAppliance.FRIDGE_FREEZER,
            MieleAppliance.WINE_CABINET,
            MieleAppliance.WINE_CONDITIONING_UNIT,
            MieleAppliance.WINE_STORAGE_CONDITIONING_UNIT,
            MieleAppliance.WINE_CABINET_FREEZER,
        ),
        description=MieleClimateDescription(
            key="thermostat",
            value_fn=(
                lambda value: _get_temperature_value(value.state_temperatures, 0)
            ),
            target_fn=(
                lambda value: _get_temperature_value(value.state_target_temperature, 0)
            ),
            zone=1,
        ),
    ),
    MieleClimateDefinition(
        types=(
            MieleAppliance.FRIDGE,
            MieleAppliance.FREEZER,
            MieleAppliance.FRIDGE_FREEZER,
            MieleAppliance.WINE_CABINET,
            MieleAppliance.WINE_CONDITIONING_UNIT,
            MieleAppliance.WINE_STORAGE_CONDITIONING_UNIT,
            MieleAppliance.WINE_CABINET_FREEZER,
        ),
        description=MieleClimateDescription(
            key="thermostat2",
            value_fn=(
                lambda value: _get_temperature_value(value.state_temperatures, 1)
            ),
            target_fn=(
                lambda value: _get_temperature_value(value.state_target_temperature, 1)
            ),
            translation_key="zone_2",
            zone=2,
        ),
    ),
    MieleClimateDefinition(
        types=(
            MieleAppliance.FRIDGE,
            MieleAppliance.FREEZER,
            MieleAppliance.FRIDGE_FREEZER,
            MieleAppliance.WINE_CABINET,
            MieleAppliance.WINE_CONDITIONING_UNIT,
            MieleAppliance.WINE_STORAGE_CONDITIONING_UNIT,
            MieleAppliance.WINE_CABINET_FREEZER,
        ),
        description=MieleClimateDescription(
            key="thermostat3",
            value_fn=(
                lambda value: _get_temperature_value(value.state_temperatures, 2)
            ),
            target_fn=(
                lambda value: _get_temperature_value(value.state_target_temperature, 2)
            ),
            translation_key="zone_3",
            zone=3,
        ),
    ),
)

ZONE1_DEVICES = {
    MieleAppliance.FRIDGE: DEVICE_TYPE_TAGS[MieleAppliance.FRIDGE],
    MieleAppliance.FRIDGE_FREEZER: DEVICE_TYPE_TAGS[MieleAppliance.FRIDGE],
    MieleAppliance.FREEZER: DEVICE_TYPE_TAGS[MieleAppliance.FREEZER],
}


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: MieleConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the climate platform."""
    coordinator = config_entry.runtime_data
    added_devices: set[str] = set()

    def _async_add_new_devices() -> None:
        nonlocal added_devices

        new_devices_set, current_devices = coordinator.async_add_devices(added_devices)
        added_devices = current_devices

        async_add_entities(
            MieleClimate(coordinator, device_id, definition.description)
            for device_id, device in coordinator.data.devices.items()
            for definition in CLIMATE_TYPES
            if (
                device_id in new_devices_set
                and device.device_type in definition.types
                and (
                    definition.description.value_fn(device)
                    not in DISABLED_TEMP_ENTITIES
                )
            )
        )

    config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices))
    _async_add_new_devices()


class MieleClimate(MieleEntity, ClimateEntity):
    """Representation of a climate entity."""

    entity_description: MieleClimateDescription
    _attr_precision = PRECISION_WHOLE
    _attr_temperature_unit = UnitOfTemperature.CELSIUS
    _attr_target_temperature_step = 1.0
    _attr_hvac_modes = [HVACMode.COOL]
    _attr_hvac_mode = HVACMode.COOL
    _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE

    @property
    def current_temperature(self) -> float | None:
        """Return the current temperature."""
        return cast(float, self.entity_description.value_fn(self.device))

    def __init__(
        self,
        coordinator: MieleDataUpdateCoordinator,
        device_id: str,
        description: MieleClimateDescription,
    ) -> None:
        """Initialize the climate entity."""
        super().__init__(coordinator, device_id, description)

        t_key = self.entity_description.translation_key

        if description.zone == 1:
            t_key = ZONE1_DEVICES.get(
                cast(MieleAppliance, self.device.device_type), "zone_1"
            )
            if self.device.device_type in (
                MieleAppliance.FRIDGE,
                MieleAppliance.FREEZER,
            ):
                self._attr_name = None

        if description.zone == 2:
            t_key = "zone_2"
            if self.device.device_type in (
                MieleAppliance.FRIDGE_FREEZER,
                MieleAppliance.WINE_CABINET_FREEZER,
            ):
                t_key = DEVICE_TYPE_TAGS[MieleAppliance.FREEZER]

        elif description.zone == 3:
            t_key = "zone_3"

        self._attr_translation_key = t_key
        self._attr_unique_id = f"{device_id}-{description.key}-{description.zone}"

    @property
    def target_temperature(self) -> float | None:
        """Return the target temperature."""

        return cast(float | None, self.entity_description.target_fn(self.device))

    @property
    def max_temp(self) -> float:
        """Return the maximum target temperature."""
        if len(self.action.target_temperature) < self.entity_description.zone:
            return super().max_temp
        return cast(
            float,
            self.action.target_temperature[self.entity_description.zone - 1].max,
        )

    @property
    def min_temp(self) -> float:
        """Return the minimum target temperature."""
        if len(self.action.target_temperature) < self.entity_description.zone:
            return super().min_temp
        return cast(
            float,
            self.action.target_temperature[self.entity_description.zone - 1].min,
        )

    async def async_set_temperature(self, **kwargs: Any) -> None:
        """Set new target temperature."""
        try:
            await self.api.set_target_temperature(
                self._device_id,
                cast(float, kwargs.get(ATTR_TEMPERATURE)),
                self.entity_description.zone,
            )
        except ClientResponseError as err:
            _LOGGER.debug("Error setting climate state for %s: %s", self.entity_id, err)
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="set_state_error",
                translation_placeholders={
                    "entity": self.entity_id,
                },
            ) from err
        await self.coordinator.async_request_refresh()
